/*
 * Copyright 1999-2011 Alibaba Group.
 *  
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *  
 *      http://www.apache.org/licenses/LICENSE-2.0
 *  
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.alibaba.dubbo.remoting.http.jetty;

import java.io.File;
import java.util.Map;

import org.apache.catalina.startup.Tomcat;
import org.mortbay.jetty.AbstractConnector;
import org.mortbay.jetty.Server;
import org.mortbay.jetty.bio.SocketConnector;
import org.mortbay.jetty.nio.SelectChannelConnector;
import org.mortbay.jetty.security.SslSocketConnector;
import org.mortbay.jetty.servlet.ServletHandler;
import org.mortbay.jetty.servlet.ServletHolder;
import org.mortbay.thread.QueuedThreadPool;

import com.alibaba.dubbo.common.Constants;
import com.alibaba.dubbo.common.URL;
import com.alibaba.dubbo.common.configure.ExtensionConfiguratorUtils;
import com.alibaba.dubbo.common.logger.Logger;
import com.alibaba.dubbo.common.logger.LoggerFactory;
import com.alibaba.dubbo.common.utils.NetUtils;
import com.alibaba.dubbo.remoting.http.HttpHandler;
import com.alibaba.dubbo.remoting.http.servlet.DispatcherServlet;
import com.alibaba.dubbo.remoting.http.servlet.StaticResourceServlet;
import com.alibaba.dubbo.remoting.http.support.AbstractHttpServer;

public class JettyHttpServer extends AbstractHttpServer {

    private static final Logger logger = LoggerFactory.getLogger(JettyHttpServer.class);

    private Server              server;

    @SuppressWarnings("unchecked")
	public JettyHttpServer(URL url, final HttpHandler handler){
        super(url, handler);
        DispatcherServlet.addHttpHandler(url.getPort(), handler);
        
        int threads = url.getParameter(Constants.THREADS_KEY, Constants.DEFAULT_THREADS);
        QueuedThreadPool threadPool = new QueuedThreadPool();
        threadPool.setDaemon(true);
        threadPool.setMaxThreads(threads);
        threadPool.setMinThreads(threads);

        AbstractConnector connector = null;
    	ExtensionConfiguratorUtils utils = ExtensionConfiguratorUtils.getInstance();
        if(url.getProtocol().equals("https")){
        	SslSocketConnector conn = new SslSocketConnector();
	        conn.setKeystore(utils.getStringValue(new String[]{"jetty.https.keystoreFile", "jetty.https.keystore-file"}));
	        conn.setKeystoreType(utils.getStringValue(new String[]{"jetty.https.keystoreType", "jetty.https.keystore-type"}, "PKCS12"));
	        conn.setPassword(utils.getStringValue(new String[]{"jetty.https.keystorePassword", "jetty.https.keystore-password"}));
        	conn.setTruststore(utils.getStringValue(new String[]{"jetty.https.trustKeystoreFile", "jetty.https.trust-keystore-file"}));
        	conn.setTrustPassword(utils.getStringValue(new String[]{"jetty.https.trustKeystorePassword", "jetty.https.trust-keystore-password"}));
        	conn.setTruststoreType(utils.getStringValue(new String[]{"jetty.https.trustKeystoreType", "jetty.https.trust-keystore-type"}, "PKCS12"));
        	//conn.setSslKeyManagerFactoryAlgorithm(utils.getStringValue(new String[]{"jetty.https.sslProtocol", "jetty.https.ssl-protocol"}, "TLS"));
        	conn.setKeyPassword(utils.getStringValue(new String[]{"jetty.https.keyPass", "jetty.https.key-pass", "jetty.https.keyPassword", "jetty.https.key-password"}));
        	 
        	conn.setNeedClientAuth(false);
        	conn.setWantClientAuth(true);
        	connector = conn;
        }else{
	        connector = new SelectChannelConnector();
	       
	    }
        if (! url.isAnyHost() && NetUtils.isValidLocalHost(url.getHost())) {
            connector.setHost(url.getHost());
        }
        connector.setPort(url.getPort());

        server = new Server();
        server.setThreadPool(threadPool);
        server.addConnector(connector);
        
        ServletHandler servletHandler = new ServletHandler();
        
        Object mapping = utils.getValue(new String[]{"http.servlet.mapping"});
        // virtual -> real
        if(mapping != null && mapping instanceof Map){
         	Map<Object, Object> m = (Map<Object, Object>)mapping;
         	String s = File.separator;
        	for(Map.Entry<Object, Object> entry : m.entrySet()){
        		String key = entry.getKey().toString().trim();
        		String value = entry.getValue() == null?null:entry.getValue().toString().trim();
        		if(value == null || value.length() == 0) continue;
        		
        		if(!key.startsWith("/")){
        			key = "/" + key;
        		}
        		if(!key.endsWith("/")) key += "/";
        		//根目录不可以
        		if(key.equals("/") || key.equals("/dubbo/")) continue;
        		
        		if(!s.equals("/")){
        			value = value.replace("/", s);
        		}
        		if(!value.endsWith(s)) value += s;
        		
        		StaticResourceServlet.addServletMapping(key, value);
        		ServletHolder holder = servletHandler.addServletWithMapping(StaticResourceServlet.class, key + "*");
        	}
        }
        
        ServletHolder servletHolder = servletHandler.addServletWithMapping(DispatcherServlet.class, "/*");
        servletHolder.setInitOrder(2);
        
        server.addHandler(servletHandler);
        
        try {
            server.start();
        } catch (Exception e) {
            throw new IllegalStateException("Failed to start jetty server on " + url.getAddress() + ", cause: "
                                            + e.getMessage(), e);
        }
    }

    public void close() {
        super.close();
        if (server != null) {
            try {
                server.stop();
            } catch (Exception e) {
                logger.warn(e.getMessage(), e);
            }
        }
    }

}